package capr.model;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme.math.Quaternion;
import com.md5importer.control.MD5AnimController;
import com.md5importer.control.MD5NodeController;
import com.md5importer.interfaces.control.IBlendController;
import com.md5importer.control.BlendController;
import com.md5importer.interfaces.model.IMD5Anim;
import com.md5importer.interfaces.model.IMD5Node;
import com.md5importer.interfaces.model.anim.IFrame;
import com.md5importer.test.util.ThreadedUpdater;
import com.md5importer.model.MD5Anim;

/** @author Ludwig Lienkamp 
 * 	@author Daniel Ahrens
 *  @author Daniel Erpilev
 *  @version v.0.34
 *  
 *  this Class controls the Mesh and Animation of the Model
 */

public class ControlAnimation {
	
	//handle the exceptions
    private static final Logger logger = Logger.getLogger(ControlAnimation.class.getName());
	
	protected MD5NodeController	meshController	 		= null;
	protected ArrayList<MD5AnimController> animArray 	= new  ArrayList<MD5AnimController>();
	protected ThreadedUpdater  	updater			 		= null;
	protected MD5AnimController animController			= null;
	protected IBlendController  blender					= null;
	
	/**
	 * Constructor
	 */
	public ControlAnimation(IMD5Node mesh) {
		
		//Create controller for the mesh
		meshController = new MD5NodeController(mesh);
		blender = new BlendController(mesh, meshController);
	}
	
	/**
	 * Ludwig Lienkamp, Daniel Erpilev <br>
	 *
	 * starts the animationthread
	 */
	public void startAnimation() {
		
		//only start thread after the method setAnimation()
		if (animController != null) {
			
			//Create a threaded updater to update animations in separate thread.
			updater = new ThreadedUpdater(60);
			updater.addController(animController);
			updater.addController(blender);
			updater.start();
			
		} else {
			
			logger.log(Level.SEVERE, "animController in methode startAnimationis is not initialisiert");
			System.exit(1);
		} 
	}
	
	/**
	 * Ludwig Lienkamp <br>
	 *
	 * stops the animationthread
	 */
	public void stopAnimation() {
		
		//stop animation thread
		updater.stop();
		
		//Garbage Collector free memory of the objects
		updater = null;
		animController = null;
	}
	
	public void stayAtPoint(IMD5Anim animation) {
		
		stayAtPoint(getIndexOfAnimation(animation));
	}
	
	/**
	 * Daniel Ahrens, Ludwig Lienkamp <br>
	 * 
	 * rootbone stay at rootpoint
	 *
	 * @param index 	index of animArray
	 */
	public void stayAtPoint(int index) {
		
		//only start thread if controller in animArray is initialisiert
		if (animArray.get(index) != null) {	
		
			//get the IMD5Anim from controller
			IMD5Anim animation = animArray.get(index).getAnim();
					
			//step through every frame
			for (int j = 0; j < animation.getFrameCount(); j++){
				
				//set the "next" index in IMD5Anim to return the right Frame 
				animation.setIndices(j - 1, j + 1, animation.getAnimationTime());
	
				//get the Frame
				IFrame frame = animation.getNextFrame();
	
				//set the first values to zero 
				//first values are the transformations of the rootbone
				frame.setTransform(0, 0, 0.0f);
				frame.setTransform(0, 1, 1.0f * frame.getTransformValue(0, 1));
				//frame.setTransform(0, 1, 0.0f);
				frame.setTransform(0, 2, 0.0f);
				
				//set Orientation at zero
				frame.getOrientation(0).set(new Quaternion(0,0,0,0));
			}
		
			//set the indices back to the startvalue
			animation.setIndices(0, 1, animation.getAnimationTime());
		
			//override the animController with new animation
			animArray.set(index, new MD5AnimController(animation));
			
		} 
		else {
			
			//create a logger output
			logger.log(Level.SEVERE, "animController in methode stayAtPoint is not initialisiert");
			
			//exit the System
			System.exit(1);
		} 
	}
	
	/**
	 * Daniel Ahrens <br>
	 * 
	 * cuts out a part of the Animation
	 * 
	 * @param index 	index of the animArray
	 * @param begin 	begin of the new animation
	 * @param end 		end of the new animation
	 */
	public void cutAnimation(int index, int begin, int end) {
		
		//get the IMD5Anim from controller
		IMD5Anim animation = animArray.get(index).getAnim();
		
		if (animation.getFrameCount() > end && begin >= 0){
			
			//Make an Array of Frames; length is equal to the new one
			IFrame[] frames = new IFrame[end - begin];
			
			//step thru every frame minus the cut frames 
			for (int j = begin; j < end; j++){
			
				//set the "next" index in IMD5Anim to return the right Frame 
				animation.setIndices(j - 1, j + 1, animation.getAnimationTime());

				//get the Frame and save it in a array
				frames[j - begin] = animation.getNextFrame();
			}
			//make a new animation with the new frames
			IMD5Anim animationCut = new MD5Anim(animation.getName(),animation.getJointIDs(),frames,24f);
			
			//override the animController with new animation
			animArray.set(index, new MD5AnimController(animationCut));
		}
	}
	
	/**
	 * Ludwig Lienkamp <br>
	 *
	 */
	public int getIndexOfAnimation(IMD5Anim animation) {
		
		int index = -1; 
		
		for(int i = 0; i < animArray.size(); i++) 
			
			if(animArray.get(i).getAnim().equals(animation)) 

				index = i;

		return index;
	}
	
	/**
	 * Ludwig Lienkamp <br>
	 *
	 */
	public void setAnimation(IMD5Anim animation) {	
		
		setAnimation(getIndexOfAnimation(animation));
	}
	
	/**
	 * Ludwig Lienkamp <br>
	 *
	 * switch the animation of the Mesh
	 *
	 * @param index		index of the animArray
	 */
	public void setAnimation(int index) {
		
		//stop animation before set some new
		if(animController != null)
			
			stopAnimation();
		
		animController = animArray.get(index);

		//add the animation to the meshController
		meshController.setActiveAnim(animController.getAnim());
	}
	
	/**
	 * Ludwig Lienkamp <br>
	 *
	 * add a animation to the animArray
	 *
	 * @param index		index of the animArray
	 */
	public void addAnimation(IMD5Anim anim) {
		
		//Create controller for the animation an at it to the array
		animArray.add(new MD5AnimController(anim));
	}
	
	/**
	 * Ludwig Lienkamp <br>
	 *
	 * @return the active animation 
	 */
	public IMD5Anim getActiveAnimation() {
		
		return meshController.getActiveAnim();
	}
	
	/**
	 * Ludwig Lienkamp, Daniel Erpilev <br>
	 *
	 */
	public void morphAnimation(IMD5Anim animation) {	
		
		morphAnimation(getIndexOfAnimation(animation));
	}

	/**
	 * Daniel Erpilev <br>
	 * 
	 * Blend the current Animation with next Animation from Array
	 * 
	 * @param index 	index of the blend Animation in the Array
	 */
	public void morphAnimation(int index){ 

		//reset the controller and set the animation at the first frame
		this.animArray.get(index).reset();

		//animController.isCyleComplete()
	//	animController.getAnim().
		
		//blend the current Animation with the next AnimationController
		this.blender.blend(this.animArray.get(index).getAnim(), this.animArray.get(index), 0.2f);
		
		// set the speed of the animation to original
		this.animArray.get(index).setSpeed(this.animArray.get(index).getAnim().getAnimationTime());	

	}
}
